<?php
namespace WDB\Query;
use WDB;

/**
 * Query structure base class.
 *
 * Query structure is developed for object-style building SQL queries (change any part of the query anytime!
 * not just wrap query in another like some database libraries do).
 * This base class contains structure parts which are common for select, update and delete:
 * - where condition
 * - order clause
 * - data count limit and offset
 *
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package WDB
 *
 * @property Element\iCondition $where
 * @property Element\OrderRule[] $order
 * @property int $limit
 * @property int $offset
 */
abstract class SUDQuery extends Query
{
    /**@var Element\iCondition|NULL*/
    protected $where = NULL;
    /**@var Element\OrderRule[]*/
    protected $order = array();
    /**@var int*/
    protected $limit = 0;
    /**@var int*/
    protected $offset = 0;

    // <editor-fold defaultstate="collapsed" desc="WHERE clause">

    /**
     * Add WHERE condition. Joined to current condition with AND operator.
     *
     * @param Element\iCondition condition
     * @return Query fluent interface.
     */
    public function addCondition(Element\iCondition $condition)
    {
        if ($this->where === NULL)
        {
            $this->where = $condition;
        }
        else
        {
            $this->where = Element\LogicOperator::lAnd($this->where, $condition);
        }
        return $this;
    }

    /**
     * Get current WHERE condition.
     *
     * @return Element\iCondition
     */
    public function getWhere()
    {
        return $this->where;
    }

    /**
     * Set WHERE condition. Previous condition is rewritten.
     *
     * @param Element\iCondition|NULL condition or NULL for no condition
     * @return Query fluent interface
     */
    public function setWhere(Element\iCondition $condition = NULL)
    {
        $this->where = $condition;
        return $this;
    }
    // </editor-fold>

    //<editor-fold defaultstate="collapsed" desc="ORDER BY clause">
    /**
     * Add order rule to the end of rules list.
     *
     * It will be the last rule applied - only row groups equal from previous ordering will be sorted.
     *
     * @param Element\OrderRule order rule
     * @return Query fluent interface
     */
    public function appendOrder(Element\OrderRule $order)
    {
        $this->order[] = $order;
        return $this;
    }

    /**
     * Add order rule to the beginning of rules list.
     *
     * It will be the first rule applied - all rows will be sorted by this rule first,
     * then equal groups with following rules.
     *
     * @param Element\OrderRule order rule
     * @return Query fluent interface
     */
    public function prependOrder(Element\OrderRule $order)
    {
        array_unshift($this->order, $order);
        return $this;
    }

    /**
     * Get array of all order rules.
     *
     * @return Element\OrderRule[]
     */
    public function getOrder()
    {
        return $this->order;
    }

    /**
     * Set all order rules as array (previous rules are rewritten)
     *
     * @param Element\OrderRule[] array of rules
     * @return Query  fluent interface
     */
    public function setOrder(array $orders)
    {
        $this->order = array();
        if (is_array($orders))
        {
            foreach ($orders as $order)
            {
                $this->appendOrder($order);
            }
        }
        else
        {
            $this->appendOrder($orders);
        }
        return $this;
    }
    //</editor-fold>

    // <editor-fold defaultstate="collapsed" desc="LIMIT / OFFSET clause">
    /**
     * Set maximum limit how many records will be retrieved in the query. Default is 0 which means no limit.
     *
     * @param int limit
     * @return Query fluent interface
     */
    public function setLimit($value)
    {
        $this->limit = intval($value);
        return $this;
    }

    /**
     * Get record limit.
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Set from which offset in full result set we will start returning rows. Default is 0 (beginning)
     *
     * @param int
     * @return Query fluent interface
     */
    public function setOffset($value)
    {
        $this->offset = intval($value);
        return $this;
    }

    /**
     * Get from which offset in full result set we will start returning rows.
     *
     * @return int
     */
    public function getOffset()
    {
        return $this->offset;
    }
    // </editor-fold>


    public function filter($condition)
    {
        if (!is_array($condition)) $condition = func_get_args();
        foreach ($condition as $key=>$val)
        {
            if ($val instanceof WDB\Structure\FilterRule)
            {
                $column = Element\ColumnIdentifier::parse($val->column);
                $val = $val->value;
            }
            else
            {
                $column = Element\ColumnIdentifier::parse($key);
            }
            if (!$val instanceof Element\Datatype\iDatatype)
            {
                $val = Element\Datatype\AbstractType::createDatatype($val);
            }
            $this->addCondition(Element\Compare::Equals($column, $val));
        }
        return $this;
    }

    public function not(array $condition)
    {
        foreach ($condition as $key=>$val)
        {
            $this->addCondition(Element\Compare::NEquals(Element\ColumnIdentifier::parse($key), WDB\Query\Element\Datatype\AbstractType::createDatatype($val)));
        }
        return $this;
    }
}